﻿/*
 * Copyright (c) 2009, Thiago M. Sayao <thiago.sayao@gmail.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the author nor the names of its contributors may be 
 *    used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Globalization;
using System.Web.Security;
using System.Web.Mvc;
using System.Net.Mail;
using System.Collections.Generic;
using DotNetBurner.Core;
using DotNetBurner.Web;
using DotNetBurner.Web.Settings;
using Resources;

namespace DotNetBurner
{
    [DotNetBurner.Web.HandleError]
    public class UserController : BaseController
    {

        [CompressFilter, AcceptVerbs (HttpVerbs.Get | HttpVerbs.Head)]
        public ActionResult Logon(string returnUrl)
        {
            var viewData = GetViewData<LogOnViewData>();
            viewData.ReturnUrl = returnUrl;

            return View("Logon", viewData);
        }

        [CompressFilter, AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken(Salt = "logon")]
        public JsonResult Logon(string userName, string password, bool? rememberMe, string returnUrl)
        {
            JsonResult res = new JsonResult();

            List<string> errors = new List<string>();

            if (string.IsNullOrEmpty(userName))
                errors.Add(Strings.UserCannotBeBlank);

            if (string.IsNullOrEmpty(password.Trim()))
                errors.Add(Strings.PassCannotBeBlank);

            if (errors.Count == 0 && !UserManager.ValidateUser(userName, password))
                errors.Add(Strings.InvalidLogOn);

            //All OK, submit
            if (errors.Count == 0)
            {
                try
                {
                    if (!rememberMe.HasValue)
                        rememberMe = false;

                    FormsAuthentication.SetAuthCookie(userName, rememberMe.Value);
                }
                catch (NullReferenceException)
                {
                }
            }

            if (string.IsNullOrEmpty (returnUrl))
                returnUrl = Url.RouteUrl("Index");

            res.Data = new
            {
                hasErrors = !(errors.Count == 0),
                errors = errors.ToArray(), 
                returnUrl
            };

            return Json(res);
        }

        [CompressFilter, AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Logoff()
        {
            if (IsUserAuthenticated)
                FormsAuthentication.SignOut();

            return RedirectToRoute("Index");
        }

        [CompressFilter, AcceptVerbs (HttpVerbs.Post)]
        public JsonResult SendPassword(string email)
        {
            List<string> errors = new List<string>();
            List<string> messages = new List<string>();

            if (string.IsNullOrEmpty(email))
            {
                errors.Add(Strings.EmailCannotBeBlank);
            }
            else if (!email.IsValidEmail())
            {
                errors.Add(Strings.InvalidEmail);
            }
            else
            {
                var userName = UserManager.GetUserNameByEmail(email);

                if (string.IsNullOrEmpty(userName))
                {
                    errors.Add(Strings.EmailNotFound);
                }
                else
                {
                    var user = UserManager.GetUser(userName, false);
                    var password = user.ResetPassword ();

                    try
                    {
                        SendPasswordMail (user.Email, password);
                        messages.Add (Strings.PassSentToEmail);
                    }
                    catch (SmtpException exception)
                    {
                        Log.Exception(exception);
                        errors.Add(Strings.SendPasswordFailed);
                    }
                }
            }

            var res = new JsonResult();

            res.Data = new
            {
                hasErrors = !(errors.Count == 0),
                errors = errors.ToArray(), 
                messages
            };

            return Json(res);
        }

        [CompressFilter, AcceptVerbs (HttpVerbs.Get | HttpVerbs.Head)]
        public ActionResult Signup()
        {
            return View("Signup", GetViewData<BaseViewData>());
        }

        [CompressFilter, ReCaptchaValidatorFilter, AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken(Salt = "signup")]
        public JsonResult Signup(string userName, string password, string passwordConfirm, string email, bool captchaValid)
        {
            List<string> errors = new List<string>();

            if (string.IsNullOrEmpty(userName))
                errors.Add(Strings.UserCannotBeBlank);
            else if (!userName.IsValidUserName())
                errors.Add(Strings.UserNameOnlyLettersDigits);

            if (string.IsNullOrEmpty(password))
                errors.Add(Strings.PassCannotBeBlank);
            else if (password != passwordConfirm)
                errors.Add(Strings.PasswordConfirmDoesNotMatch);

            if (password.Length < UserManager.MinRequiredPasswordLength)
                errors.Add(Strings.InvalidPassLength.FormatWith(UserManager.MinRequiredPasswordLength));

            if (string.IsNullOrEmpty(email))
                errors.Add(Strings.EmailCannotBeBlank);
            else if (!email.IsValidEmail())
                errors.Add(Strings.InvalidEmail);

            if (!captchaValid)
                errors.Add(Strings.CaptchaInvalid);

            if(errors.Count == 0)
            {
                try
                {
                    MembershipCreateStatus status;

                    UserManager.CreateUser(userName, password, email, null, null, true, null, out status);

                    switch (status)
                    {
                        case MembershipCreateStatus.DuplicateEmail:
                            errors.Add(Strings.EmailInUse);
                            break;
                        case MembershipCreateStatus.DuplicateUserName:
                            errors.Add(Strings.UserNameInUse);
                            break;
                        case MembershipCreateStatus.InvalidEmail:
                            errors.Add(Strings.InvalidEmail);
                            break;
                        case MembershipCreateStatus.InvalidPassword:
                            errors.Add(Strings.InvalidPassLength
                                        .FormatWith(UserManager.MinRequiredPasswordLength));
                            break;
                        case MembershipCreateStatus.InvalidUserName:
                            errors.Add(Strings.UserNameOnlyLettersDigits);
                            break;
                        case MembershipCreateStatus.Success:
                            FormsAuthentication.SetAuthCookie(userName, false);

                            try
                            {
                                SendSignupMail(userName, email);
                            }
                            catch (SmtpException exception)
                            {
                                Log.Exception(exception);
                            }

                            break;
                        default:
                            break;
                    }
                }
                catch (MembershipCreateUserException exception)
                {
                    Log.Exception(exception);
                    errors.Add(Strings.CreateUserFailed);
                }
            }

            JsonResult res = new JsonResult { Data = new { errors, hasErrors = (errors.Count != 0) } };

            return Json(res);
        }


        [CompressFilter, AcceptVerbs (HttpVerbs.Get | HttpVerbs.Head), Authorize]
        public ActionResult Profile()
        {
            return View("Profile", GetProfileSettingsViewData<ProfileSettingsViewData>());
        }

        [CompressFilter, AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken(Salt = "change-email")]
        public JsonResult ChangeEmail(string email)
        {
            List<string> errors = new List<string>();
            List<string> messages = new List<string>();

            if (!IsUserAuthenticated)
                errors.Add(Strings.NotAuthenticated);

            if (string.IsNullOrEmpty(email))
                errors.Add(Strings.EmailCannotBeBlank);
            else if (!email.IsValidEmail())
                errors.Add(Strings.InvalidEmail);
            else if(!String.IsNullOrEmpty(UserManager.GetUserNameByEmail(email)))
                errors.Add(Strings.EmailInUse);

            if (errors.Count == 0)
            {
                MembershipUser user = UserManager.GetUser(User.Identity.Name, false);
                user.Email = email;

                UserManager.UpdateUser(user);
                messages.Add(Strings.EmailSuccessfullyChanged);
            }

            JsonResult res = new JsonResult { Data = new { errors, messages, hasErrors = (errors.Count != 0) } };

            return Json(res);
        }

        [CompressFilter, AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken(Salt = "change-password")]
        public JsonResult ChangePassword(string currentPass, string password, string passwordConfirm)
        {
            List<string> errors = new List<string>();
            List<string> messages = new List<string>();

            if(!IsUserAuthenticated)
                errors.Add(Strings.NotAuthenticated);
            
            if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(currentPass))
                errors.Add(Strings.PassCannotBeBlank);
            else if (password != passwordConfirm)
                errors.Add(Strings.PasswordConfirmDoesNotMatch);
            if (password.Length < UserManager.MinRequiredPasswordLength)
                errors.Add(Strings.InvalidPassLength.FormatWith(UserManager.MinRequiredPasswordLength));

            if (!UserManager.ValidateUser(User.Identity.Name, currentPass))
                errors.Add(Strings.InvalidLogOn);

            if (errors.Count == 0)
            {
                MembershipUser user = UserManager.GetUser(User.Identity.Name, false);

                if (user.ChangePassword(currentPass, password))
                    messages.Add(Strings.PassSuccessfullyChanged);
                else
                    errors.Add(Strings.ChangePasswordFailed);
            }

            JsonResult res = new JsonResult { Data = new { errors, messages, hasErrors = (errors.Count != 0) } };

            return Json(res);
        }

        static void SendPasswordMail(string email, string password)
        {
            string body = SwissKnife.GetMailTemplateForLang("Password");
            body = body.Replace("<%Password%>", password);

            SendMail(email, SiteSettings.Settings.Name, body);
        }

        static void SendSignupMail(string userName, string email)
        {
            string body = SwissKnife.GetMailTemplateForLang("Signup");

            body = body.Replace("<%UserName%>", userName);

            SendMail(email, SiteSettings.Settings.Name, body);
        }

        T GetProfileSettingsViewData<T>() where T : ProfileSettingsViewData, new()
        {
            var viewData = GetViewData<T>();

            return viewData;
        }
    }
}